﻿// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

// ReSharper disable CheckNamespace
// ReSharper disable CommentTypo
// ReSharper disable IdentifierTypo
// ReSharper disable InconsistentNaming
// ReSharper disable MemberCanBePrivate.Global

/* .cs --
 * Ars Magna project, http://arsmagna.ru
 */

#region Using directives

using System;

#endregion

#nullable enable

namespace AM.Linguistics.Hunspell.Infrastructure;

internal ref struct SimulatedCString
{
    public SimulatedCString (string text)
    {
        buffer = text.ToCharArray();
        cachedSpan = buffer.AsSpan();
        cachedString = null!;
        cacheRequiresRefresh = true;
    }

    private char[] buffer;
    private string cachedString;
    private Span<char> cachedSpan;
    private bool cacheRequiresRefresh;

    public char this [int index]
    {
        get => index < 0 || index >= buffer.Length ? '\0' : buffer[index];
        set
        {
            ResetCache();
            buffer[index] = value;
        }
    }

    public int BufferLength => buffer.Length;

    public void WriteChars (string text, int destinationIndex)
    {
        ResetCache();

        var neededLength = text.Length + destinationIndex;
        if (buffer.Length < neededLength) Array.Resize (ref buffer, neededLength);

        text.CopyTo (0, buffer, destinationIndex, text.Length);
    }

    public void WriteChars (ReadOnlySpan<char> text, int destinationIndex)
    {
        ResetCache();

        var neededLength = text.Length + destinationIndex;
        if (buffer.Length < neededLength) Array.Resize (ref buffer, neededLength);

        text.CopyTo (buffer.AsSpan (destinationIndex));
    }

    public void Assign (string text)
    {
#if DEBUG
        if (text == null) throw new ArgumentNullException (nameof (text));
        if (text.Length > buffer.Length) throw new ArgumentOutOfRangeException (nameof (text));
#endif
        ResetCache();

        text.CopyTo (0, buffer, 0, text.Length);

        if (text.Length < buffer.Length) Array.Clear (buffer, text.Length, buffer.Length - text.Length);
    }

    public void Destroy()
    {
        ResetCache();
        buffer = null!;
    }

    public override string ToString()
    {
        return cachedString ??= GetTerminatedSpan().ToString();
    }

    public Span<char> GetTerminatedSpan()
    {
        if (cacheRequiresRefresh)
        {
            cacheRequiresRefresh = false;
            cachedSpan = buffer.AsSpan (0, FindTerminatedLength());
        }

        return cachedSpan;
    }

    private void ResetCache()
    {
        cacheRequiresRefresh = true;
        cachedString = null!;
    }

    private int FindTerminatedLength()
    {
        var length = Array.IndexOf (buffer, '\0');
        return length < 0 ? buffer.Length : length;
    }
}
